// jsx / template
const nodeTemplate = `
  <div class="msg" id="msg">
    <span style="color: red;">hello</span>
    <i>world</i>
    <h1>{ counter }</h1>
  </div>
`;

let state = {
  counter: 0,
};

// react
// export function setState (newState) {
//   Object.assign(state, newState);
//   render();
// }

// vue2: defineProperty
// const midSang = { ...state };
// Object.defineProperty(state, 'counter', {
//   set (newValue) {
//     midSang.counter = newValue;
//     render();
//   },
//   get () {
//     return midSang.counter;
//   },
// });

const watchCallbackList = [];

// vue3: Proxy
state = new Proxy(state, {
  get (obj, prop) {
    // state.counter
    // obj: state, prop: counter
    return obj[prop];
  },
  set (obj, prop, newValue) {
    // obj: state, prop: counter
    obj[prop] = newValue;
    watchCallbackList.forEach(cb => cb());
    render();
    return true;
  },
});

setInterval(() => {
  // react
  // setState({
  //   counter: state.counter + 1,
  // });
  // vue
  state.counter++;
}, 1000);

// template >>> vDOM
const node = {
  tag: 'div',
  attrs: {
    class: 'msg',
    id: 'msg',
  },
  // content: 'hello world',
  children: [
    {
      tag: 'span',
      attrs: {
        style: 'color: red;',
      },
      content: 'hello',
    },
    {
      tag: 'i',
      attrs: {},
      content: 'world',
    },
    {
      tag: 'h1',
      attrs: {},
      content: () => state.counter,
    },
  ],
};

// interface VNode {
//   tag: string;
//   attrs: Attrs;
//   ...
// }

let lifeCycles = {

};

let inited = false;

/**
 * @param { HTMLElement } parentNode
 * @param { VNode } vNode
 */
function render () {
  if (!inited) {
    lifeCycles.onBeforeMount && lifeCycles.onBeforeMount();
  } else {
    lifeCycles.onBeforeUpdate && lifeCycles.onBeforeUpdate();
  }
  const root = document.getElementById('root');
  root.innerHTML = '';
  function innerRender (parentNode, vNode) {
    const realNode = document.createElement(vNode.tag);
    for (const attrKey in vNode.attrs) {
      realNode.setAttribute(attrKey, vNode.attrs[attrKey]);
    }
    if (vNode.children) {
      for (const child of vNode.children) {
        innerRender(realNode, child);
      }
    } else {
      const { content } = vNode;
      realNode.innerText = typeof content === 'string'
        ? content
        : content();
    }
    parentNode.appendChild(realNode);
  }
  innerRender(root, node);
  if (!inited) {
    lifeCycles.onMounted && lifeCycles.onMounted();
    inited = true;
  } else {
    lifeCycles.onUpdated && lifeCycles.onUpdated();
  }
}

function onBeforeUpdate (cb) {
  lifeCycles.onBeforeUpdate = cb;
}

function onUpdated (cb) {
  lifeCycles.onUpdated = cb;
}

function onBeforeMount (cb) {
  lifeCycles.onBeforeMount = cb;
}

function onMounted (cb) {
  lifeCycles.onMounted = cb;
}

function watchEffect (cb) {
  watchCallbackList.push(cb);
}

